package com.libgkd;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.net.Socket;
import java.util.Hashtable;
import java.util.Vector;

import javax.swing.JOptionPane;

import com.peterswing.CommonLib;

public class LibGKD implements Runnable {
	private int port;
	private String ip;
	Socket socket = null;
	BufferedReader in = null;
	PrintWriter out = null;

	Socket statusSocket = null;
	BufferedReader statusIn = null;
	PrintWriter statusOut = null;
	boolean running;

	public LibGKD(String ip, int port) {
		this.ip = ip;
		this.port = port;
	}

	//	public void connect() {
	//		try {
	//			socket = new Socket(ip, port);
	//			in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
	//			out = new PrintWriter(socket.getOutputStream(), true);
	//
	//			
	//		} catch (Exception e) {
	//			e.printStackTrace();
	//		}
	//	}

	public void close() {
		try {
			socket.close();
			statusSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public String _continue() {
		String r = sendCommand(ip, port, "c");
		running = true;
		new Thread(this).start();
		return r;
	}

	public String pause() {
		String r = sendCommand(ip, port, "p");
		return r;
	}

	public boolean isRunning() {
		return running;
	}

	public String singleStep() {
		Vector<Breakpoint> breakpoints = listBreakpoint();
		for (Breakpoint breakpoint : breakpoints) {
			boolean r = deletePhysicalBreakpoint(breakpoint.addr);
			if (!r) {
				JOptionPane.showMessageDialog(null, "delete physical breakpoint error", "Error", JOptionPane.ERROR_MESSAGE);
			}
		}

		String r = sendCommand(ip, port, "s");

		for (Breakpoint breakpoint : breakpoints) {
			physicalBreakpoint(breakpoint.addr);
		}
		return r;
	}

	public Hashtable<String, Long> register() {
		String r = sendCommand(ip, port, "r");
		String arr[] = r.split(",");
		Hashtable<String, Long> ht = new Hashtable<String, Long>();
		for (String str : arr) {
			String temp[] = str.split("=");
			ht.put(temp[0], Long.parseLong(temp[1], 16));
		}
		return ht;
	}

	private String sendCommand(String ip, int port, String command) {
		try {
			int tryCount = 0;
			String inStr;
			do {
				if (socket == null || tryCount > 0) {
					socket = new Socket(ip, port);
					in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
					out = new PrintWriter(socket.getOutputStream(), true);

					statusSocket = new Socket(ip, port + 1);
					statusIn = new BufferedReader(new InputStreamReader(statusSocket.getInputStream()));
					statusOut = new PrintWriter(statusSocket.getOutputStream(), true);
				}
				out.print(command + "\n");
				out.flush();

				inStr = readFromQEMU();
				tryCount++;
			} while (inStr == null && tryCount <= 3);
			return inStr;
		} catch (Exception e) {
			e.printStackTrace();
			return e.getMessage();
		}
	}

	public String readFromQEMU() {
		try {
			char temp[] = new char[4];
			in.read(temp);
			int noOfBytes = Integer.parseInt(new String(temp).trim());
			temp = new char[noOfBytes];
			in.read(temp);
			String inStr = new String(temp);
			return inStr;
		} catch (Exception ex) {
			ex.printStackTrace();
			return null;
		}
	}

	public String readFromQEMUStatusPort() {
		try {
			char temp[] = new char[4];
			statusIn.read(temp);
			int noOfBytes = Integer.parseInt(new String(temp).trim());
			temp = new char[noOfBytes];
			statusIn.read(temp);
			String inStr = new String(temp);
			return inStr;
		} catch (Exception ex) {
			ex.printStackTrace();
			return null;
		}
	}

	public int[] physicalMemory(long addr, int size) {
		String r = sendCommand(ip, port, "mp" + addr + "," + size);
		if (r == null) {
			return null;
		}
		String s[] = r.split(",");
		int m[] = new int[s.length];
		for (int x = 0; x < s.length; x++) {
			m[x] = Integer.parseInt(s[x], 16);
		}
		return m;
	}

	public int[] virtualMemory(long addr, int size) {
		String r = sendCommand(ip, port, "mv" + addr + "," + size);
		if (r == null) {
			return null;
		}
		String s[] = r.split(",");
		int m[] = new int[s.length];
		for (int x = 0; x < s.length; x++) {
			m[x] = Integer.parseInt(s[x], 16);
		}
		return m;
	}

	public boolean physicalBreakpoint(long addr) {
		String r = sendCommand(ip, port, "pb" + addr);
		if (r == null) {
			return false;
		} else {
			return true;
		}
	}

	//
	//	public boolean sendBreakpoints() {
	//		for (long breakpoint : breakpoints) {
	//			String r = sendCommand(ip, port, "pb" + breakpoint);
	//			if (r == null) {
	//				return false;
	//			}
	//			if (r.equals("error")) {
	//				return false;
	//			}
	//		}
	//		return true;
	//	}

	public boolean deletePhysicalBreakpoint(long addr) {
		String r = sendCommand(ip, port, "delpb" + addr);
System.out.println(r);
		if (r == null) {
			return false;
		}
		if (r.contains("error")) {
			return false;
		} else {
			return true;
		}
	}

	public boolean deleteAllPhysicalBreakpoint() {
		String r = sendCommand(ip, port, "delpbs");
		if (r == null) {
			return false;
		}
		if (r.equals("error")) {
			return false;
		} else {
			return true;
		}
	}

	public Vector<Breakpoint> listBreakpoint() {
		Vector<Breakpoint> breakpoints = new Vector<Breakpoint>();
		String r = sendCommand(ip, port, "lb");
		String lines[] = r.split("\n");
		for (String line : lines) {
			String t[] = line.trim().split(",");
			if (t.length == 2) {
				Breakpoint bp = new Breakpoint(CommonLib.string2long(t[0]), Integer.parseInt(t[1]));
				breakpoints.add(bp);
			}
		}
		return breakpoints;
	}

	byte[] convertIntArrayToByteArray(int x[]) {
		byte t[] = new byte[x.length];
		for (int z = 0; z < x.length; z++) {
			t[z] = (byte) x[z];
		}
		return t;
	}

	@Override
	public void run() {
		while (true) {
			String s = readFromQEMUStatusPort();
			if (s != null && s.equals("paused")) {
				running = false;
				return;
			}
		}

	}

}
